package com.ford.microservices.accounts;

import com.ford.microservices.common.AccountDto;
import com.ford.microservices.common.SignUpResponse;
import com.ford.microservices.exceptions.AccountException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Collector;

import static java.util.stream.Collectors.toList;

@RestController
public class AccountsController {

    protected Logger logger = Logger.getLogger(AccountsController.class
            .getName());
    protected AccountRepository accountRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * Create an instance plugging in the respository of Accounts.
     *
     * @param accountRepository An account repository implementation.
     */
    @Autowired
    public AccountsController(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
    }

    /**
     * Fetch an account with the specified account id.
     *
     * @param id account number.
     * @return The account if found.
     * @throws AccountException If is not recognised.
     */
    @RequestMapping("/accounts/{id}")
    public Account byId(@PathVariable("id") Long id) {

        logger.info("accounts-service byId() invoked: " + id);
        Optional<Account> account = accountRepository.findById(id);
        logger.info("accounts-service byId() found: " + account);
        return account.orElseThrow(() -> new AccountException("No Such Account " + id));
    }

    @RequestMapping("/accounts/query/{cellPhone}")
    public Account byCellPhone(@PathVariable("cellPhone") String cellPhone) {
        logger.info("accounts-service byCellPhone() invoked: " + cellPhone);
        Account account = accountRepository.findByCellPhone(cellPhone);
        logger.info("accounts-service byCellPhone() found: " + account);
        return account;
    }

    /**
     * Sign up
     */
    @PostMapping("/signUp")
    public SignUpResponse signUp(@RequestBody AccountDto account) {
        SignUpResponse signUpResponse = new SignUpResponse();
        String phoneNumber = account.getCellPhone();
        String regex = "^1[3|4|5|8][0-9]\\d{8}$";
        if (StringUtils.isEmpty(phoneNumber) ||
                !phoneNumber.matches(regex)) {
            signUpResponse.setError("Invalid Cell Phone " + phoneNumber);
            return signUpResponse;
        }
        if (StringUtils.isEmpty(account.getName())) {
            signUpResponse.setError("Nick name is empty");
            return signUpResponse;
        }
        if (StringUtils.isEmpty(account.getPassword()) || account.getPassword().length() <= 8) {
            signUpResponse.setError("Password length is required(8)");
            return signUpResponse;
        }
        if (account.getRegCode() != 1234) {
            signUpResponse.setError("Invalid Reg code");
            return signUpResponse;
        }

        Account _account = accountRepository.findByCellPhone(phoneNumber);
        if (_account != null) {
            signUpResponse.setError("Cell Phone has been registered " + phoneNumber);
            return signUpResponse;
        }

        String password = account.getPassword();
        _account = new Account();
        _account.setName(account.getName());
        _account.setCellPhone(account.getCellPhone());
        _account.setRole("SALES");
        _account.setBalance(1000L);
        _account.setPassword(passwordEncoder.encode(password));
        Account saved = accountRepository.save(_account);
        saved.setUsername("ford_" + saved.getId());
        saved = accountRepository.save(saved);

        AccountDto accountDto = new AccountDto();
        accountDto.setId(saved.getId());
        accountDto.setBalance(saved.getBalance());
        accountDto.setName(saved.getName());
        accountDto.setCellPhone(saved.getCellPhone());
        signUpResponse.setAccountDto(accountDto);
        return signUpResponse;
    }

    /**
     * Checkin
     *
     * @return
     */
    @GetMapping("/checkin/{id}")
    public ResponseEntity<String> checkin(@PathVariable Long id) {
        Account _account = accountRepository.findById(id).orElseThrow(() -> new AccountException("Invalid user"));
        LocalDateTime lastCheckinTime = _account.getLastCheckInTime();
        boolean allowToCheckin = false;
        if (lastCheckinTime == null) {
            allowToCheckin = true;
        } else {
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime noon = LocalDate.now().atTime(LocalTime.NOON);
            if (lastCheckinTime.until(now, ChronoUnit.DAYS) > 0) {
                allowToCheckin = true;
            } else if (lastCheckinTime.isBefore(noon) && now.isAfter(noon)) {
                allowToCheckin = true;
            }
        }
        if (allowToCheckin) {
            _account.setBalance(_account.getBalance() + 10);
            _account.setLastCheckInTime(LocalDateTime.now());
            accountRepository.save(_account);
            return new ResponseEntity<>("Checkin Successfully", HttpStatus.OK);
        } else {
            return new ResponseEntity<>("You have checked in today", HttpStatus.OK);
        }
    }

    /**
     * get sales balances
     */
    @RequestMapping("/sales")
    public List<AccountDto> getSalesBalances() {
        List<Account> allAccounts = accountRepository.findAllSalesAccount();
        return allAccounts.stream().map(account -> {
            AccountDto accountDto = new AccountDto();
            accountDto.setId(account.getId());
            accountDto.setName(account.getName());
            accountDto.setBalance(account.getBalance());
            return accountDto;
        }).collect(toList());
    }
}
